// Copyright (C) joyinter
// All rights reserved.
// 
// Author : Jiang Li
// Date   : 2004-12-30

#include "stdafx.h"
#include "svr_server.h"
#include "svr_showinfo.h"
#include "svr_userset.h"
#include "svr_user.h"
#include "db_utility.h"
#include "svr_asyncobj.h"
#include "dbmsg_defaulthandler.h"

namespace JoyServer
{

static void FormatContent(
    CString& strContent,
    UINT userId,
    UINT tempId,
    UINT kickError,
    PCSTR kickMessage)
{
    strContent.Format(
        "<root>"
        "<user user_id=\"%d\" temp_id=\"%d\" error_id=\"%d\"/>"
        "<message value=\"%s\"/>"
        "</root>",
        userId, tempId, kickError, kickMessage ? kickMessage : "");
}

// class CJoyAsyncKickUser

class CJoyAsyncKickUser : public CJoyAsyncObject
{
public:
    CJoyServer* m_server;
    UINT    m_userId;
    UINT    m_tempId;
public:

    CJoyAsyncKickUser(CJoyServer* pServer):
        m_server(pServer)
    {
        m_userId = 0;
        m_tempId = 0;
    }

    virtual void OnExecute();
};

void CJoyAsyncKickUser::OnExecute()
{
    // open online userdb session.
    CJoyDbSource& dbOUser = m_server->m_dbSetOUser.GetSource(m_userId);
    CJoyDbSession sessUser;
    if(!JoyOpenDbSession(m_server, sessUser, dbOUser))
    {
        return;
    }

    // check for username and password.
    CJoyDbCommand kickCommand;
    if(!JoyInvokeCommand(m_server, sessUser, kickCommand,
        L"battle_kick_user",
        m_server->m_serverId,
        m_server->m_serverType,
        m_userId,
        m_tempId))
    {
        return;
    }
}

// class CJoyAsyncSendKickUser

class CJoyAsyncSendKickUser : public CJoyAsyncObject
{
public:
    CJoyServer* m_server;
    UINT    m_userId;
    UINT    m_tempId;
    UINT    m_serverId;
    UINT    m_kickError;
    CString m_kickMessage;
public:

    CJoyAsyncSendKickUser(CJoyServer* pServer):
        m_server(pServer)
    {
        m_userId    = 0;
        m_tempId    = 0;
        m_serverId  = 0;
        m_kickError = 0;
    }

    virtual void OnExecute();
};

void CJoyAsyncSendKickUser::OnExecute()
{
    CString content;
    FormatContent(content,
        m_userId, m_tempId, m_kickError, (PCSTR)m_kickMessage);

    CJoyDbMsgHandler* dbMsgHandler = m_server->m_dbMsgHandler;

    if(m_serverId == 0)
    {
        UINT loginServer, mainServer, tempLoginId, tempMainId;
        if(!dbMsgHandler->GetUserServer(m_userId, loginServer, tempLoginId, mainServer, tempMainId))
        {
            return;
        }
        if(mainServer != 0)
        {
            dbMsgHandler->SendMsg(mainServer, DBMSG_UserKick, content);
        }
        if(loginServer != 0)
        {
            dbMsgHandler->SendMsg(loginServer, DBMSG_UserKick, content);
        }
    }
    else
    {
        dbMsgHandler->SendMsg(m_serverId, DBMSG_UserKick, content);
    }
}

// command fromat : 
// <root>
//   <user user_id=? temp_id=? error_id=?/>
//   <message value="..."/>
// </root>

void CJoyDbMsgDefaultHandler::OnDbMsg_UserKick(const CJoyDbMsg& msg)
{
    __super::OnDbMsg_UserKick(msg);

    if(msg.m_root == NULL)
    {
        m_server->m_showInfo->QueueTimeColorText(InfoColor_Error,
            "bad content format for userkick[%s]", (PCSTR)msg.m_content);
        return;
    }

    CJoyXmlElement* pUserElem = msg.m_root->FindElement("user");
    if(pUserElem == NULL)
    {
        m_server->m_showInfo->QueueTimeColorText(InfoColor_Error,
            "bad content format for userkick[%s]", (PCSTR)msg.m_content);
        return;
    }
    UINT userId = pUserElem->ReadIntD("user_id", 0);
    if(userId == 0)
    {
        m_server->m_showInfo->QueueTimeColorText(InfoColor_Error,
            "bad content format for userkick[%s]", (PCSTR)msg.m_content);
        return;
    }
    UINT tempId = pUserElem->ReadIntD("temp_id", 0);
    UINT errorId= pUserElem->ReadIntD("error_id", 0);

    // copy the datas to avoid dead locks.
    CArray<CJoyUserSet*> userSets;
    {
        CJoyAutoCS gate(m_server->m_userSetsLock);
        userSets.Append(m_server->m_userSets);
    }
    int userSetsCount = (int)userSets.GetCount();
    for(int i = 0; i < userSetsCount; ++ i)
    {
        CJoyUserSet* userSet = userSets[i];
        CJoyAutoCS gate(userSet->m_userLock);
        CJoyUser* pUser;
        if(userSet->m_userIds.Lookup(userId, pUser))
        {
            if(tempId == 0 || tempId==pUser->GetTempId())
            {
                pUser->Kick(errorId);
            }
            return;
        }
    }

    // not find the user. we may remove the record directly.
    if(tempId == 0)
    {
        // this should be admin command, we need not
        // do the kick operation.
        return;
    }

    CJoyAsyncKickUser* pKickUser = new CJoyAsyncKickUser(m_server);
    pKickUser->m_userId = userId;
    pKickUser->m_tempId = tempId;
    m_server->QueueAsyncObject(pKickUser);
}

void CJoyDbMsgHandler::KickUserMainTemp(UINT userId, UINT err, UINT mainServer, UINT tempMainId)
{
    CString content;

    if(mainServer != 0)
    {
        FormatContent(content, userId, tempMainId, err, NULL);
        SendMsg(mainServer, DBMSG_UserKick, content);
    }
}

void CJoyDbMsgHandler::KickUserLoginTemp(UINT userId, UINT err)
{
    UINT loginServer, mainServer, tempLoginId, tempMainId;
    if(!GetUserServer(userId, loginServer, tempLoginId, mainServer, tempMainId))
    {
        return;
    }

    CString content;

    if(mainServer != 0)
    {
        FormatContent(content, userId, tempMainId, err, NULL);
        SendMsg(mainServer, DBMSG_UserKick, content);
    }

    if(loginServer != 0)
    {
        FormatContent(content, userId, tempLoginId, err, NULL);
        SendMsg(loginServer, DBMSG_UserKick, content);
    }
}

} // namespace JoyServer